/*
    编译运行模块：接受来自http请求中的json数据，将数据转换成源文件，编译、运行、将编译和运行结果转换成json数据
    对外提供调用接口
*/
#pragma once
#include <iostream>
#include <jsoncpp/json/json.h>
#include "compile.hpp"
#include "run.hpp"
#include "../util/log.hpp"
#include "../util/util.hpp"

namespace ns_compile_run
{
    using namespace ns_compile;
    using namespace ns_run;
    using namespace ns_util;

    class CompileRun
    {
    private:
        static std::string statusToDesc(int status) // 状态码转换为对应描述
        {
            switch (status)
            {
            case 0:
                return "编译运行正常";
            case -1:
                return "提交代码空";
            case SIGFPE:
                return "浮点数溢出";
            case SIGSEGV:
                return "段错误";
            case SIGABRT:
                return "内存资源受限";
            case SIGXCPU:
                return "cpu资源受限";
            default:
                return "未知错误";
            }
        }

    public:
        CompileRun()
        {
        }
        ~CompileRun()
        {
        }

        static bool start(const std::string &in_json, std::string &out_json)
        {
            /*
                1、收到的json数据格式
                { "code" : 用户提交的代码，
                  "input" : 用户自测用例(暂时不做处理),
                  "cpulimit" : cpu资源限制，
                  "memlimit" : 内存资源限制
                }

                2、返回的json数据格式
                { "status" : 编译或运行的状态码
                  "desc" : 状态码的描述(编译正常和异常)
                  "stdout" : 运行时标准输出
                  "stderr" : 运行时标准错误
                }
            */
            // 从json格式字符串中提取源文件代码code、cpulimit、memlimit
            Json::Value inv, outv;
            Json::Reader rd;
            Json::StyledWriter wr;
            std::string stout, sterr, comperr;
            std::string filename;
            int status, ret;
            rd.parse(in_json, inv);
            std::string code = inv["code"].asString();
            int cpulimit = inv["cpulimit"].asInt();
            int memlimit = inv["memlimit"].asInt();

            if (code.size() == 0)
            {
                LOG(ERROR) << "code没有数据\n";
                status = -1;
                goto END;
            }

            // 将code写到源文件中
            filename = FileUtil::uniqFilename(); // 形成唯一文件名
            // 添加后缀和路径
            if (!FileUtil::writeFile(PathUtil::addPath(FileUtil::addSuffixOrg(filename)), code))
            {
                LOG(ERROR) << "形成源文件失败\n";
                return false;
            }

            if (!Compile::compile(filename))
            {
                if (!FileUtil::readFile(PathUtil::addPath(FileUtil::addSuffixComperr(filename)), comperr))
                {
                    LOG(ERROR) << "编译错误文件读取失败\n";
                    return false;
                }
                status = -2;
                goto END;
            }

            status = Run::run(filename, cpulimit, memlimit);
            if (status < 0) // 内部错误
                return false;

            // 无论运行正常还是异常都有可能有标准输出
            if (!FileUtil::readFile(PathUtil::addPath(FileUtil::addSuffixStdout(filename)), stout))
            {
                LOG(ERROR) << "运行标准输出文件读取失败\n";
                return false;
            }
            
            // 读取标准错误
            if (!FileUtil::readFile(PathUtil::addPath(FileUtil::addSuffixStderr(filename)), sterr))
            {
                LOG(ERROR) << "运行标准错误文件读取失败\n";
                return false;
            }

        END:
            outv["status"] = status;
            if (status == -2) // 编译失败
            {
                outv["desc"] = comperr;
            }
            else
            {
                outv["desc"] = statusToDesc(status);
                outv["stdout"] = stout;
                outv["stderr"] = sterr;
            }
            out_json = wr.write(outv);

            // 清理临时文件
            FileUtil::clearTempfile(filename);

            return true;
        }
    };
}